package com.gcxy.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gcxy.constant.PermissionConst;
import com.gcxy.constant.StringConst;
import com.gcxy.core.PageVO;
import com.gcxy.core.R;
import com.gcxy.dto.export.DownloadFileDTO;
import com.gcxy.dto.guidance.GuidancePageDTO;
import com.gcxy.dto.guidance.UpdateGuidanceDTO;
import com.gcxy.entity.*;
import com.gcxy.mapper.GuidanceMapper;
import com.gcxy.service.*;
import com.gcxy.utils.BeanCopyUtil;
import com.gcxy.utils.DateUtil;
import com.gcxy.utils.XWPFUtil;
import com.gcxy.vo.guidance.GuidanceVO;
import com.gcxy.vo.role.RoleVO;
import com.gcxy.vo.user.UserVO;
import com.gcxy.vo.weekly.ProjectVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.gcxy.constant.StringConst.*;
import static com.gcxy.enums.ResultCodeEnum.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Huang先森
 * @since 2024-07-22
 */
@Slf4j
@Service
public class GuidanceServiceImpl extends ServiceImpl<GuidanceMapper, Guidance> implements GuidanceService {

    @Resource
    private TeacherService teacherService;

    @Autowired
    private SemesterService semesterService;

    @Autowired
    private UserService userService;

    @Autowired
    private WeeklyService weeklyService;

    @Autowired
    private ProjectService projectService;

    @Autowired
    private WeeklyProjectService weeklyProjectService;

    @Autowired
    private AppraiseService appraiseService;

    @Autowired
    private ClazzService clazzService;

    @Autowired
    private StudentTeacherService studentTeacherService;

    /**
     * 新增周指导记录
     *
     * @param agencyId
     * @return
     */
    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void addGuidance(Integer agencyId) {
        Semester semester = semesterService.getCurrentSemester();
        if (ObjectUtils.isEmpty(semester)) {
            throw new RuntimeException("组织{" + agencyId + "}当前时间不需要生成周指导记录");
        }
        List<User> honoraryTeacher = teacherService.getHonoraryTeacher(semester.getId(), agencyId);
        if (CollectionUtils.isEmpty(honoraryTeacher)) {
            throw new RuntimeException("组织{" + agencyId + "}当前时间没有教师需要生成周指导记录");
        }
        List<StudentTeacher> honoraryTeachers = studentTeacherService.getHonoraryTeachers(honoraryTeacher);
        for (StudentTeacher honoraryUser : honoraryTeachers) {
            Guidance guidance = new Guidance();
            guidance.setSemesterId(semester.getId());
            guidance.setAgencyId(agencyId);
            guidance.setWeek(semesterService.getWeekNum());
            guidance.setWeekTime(DateUtil.getWeekTime());
            guidance.setHonoraryTeacherId(honoraryUser.getHonoraryTeacherId());
            guidance.setStatus(StringConst.GUIDANCE_NOT_FINISH_STATUS);
            guidance.setUpdater(INITIALIZE_UPDATE_ADMIN);
            Guidance guidanceInfo = getGuidanceInfo(honoraryUser.getHonoraryTeacherId(), guidance.getWeek(), semester.getId());
            if (ObjectUtils.isNotEmpty(guidanceInfo)) {
                continue;
            }
            boolean save = save(guidance);
            if (!save) {
                throw new RuntimeException("组织{" + agencyId + "}新增周指导记录失败");
            } else {
                log.info("新增周指导记录成功");
            }
        }

    }

    /**
     * 修改周指导记录
     *
     * @param updateGuidanceDTO
     * @return
     */
    @Override
    public R<?> updateGuidance(UpdateGuidanceDTO updateGuidanceDTO) {
        Guidance guidance = getById(updateGuidanceDTO.getId());
        if (ObjectUtils.isEmpty(guidance)) {
            return R.Failed(DB_GET_DATA_FAIL.getCode(), "根据周指导记录id未能找到对应信息");
        }
        guidance.setGuideStudents(JSONUtil.toJsonStr(updateGuidanceDTO.getGuideStudents()));
        guidance.setDevelopmentProject(JSONUtil.toJsonStr(updateGuidanceDTO.getDevelopmentProject()));
        guidance.setProjectCompletion(JSONUtil.toJsonStr(updateGuidanceDTO.getProjectCompletion()));
        guidance.setLearningTaskCompletion(JSONUtil.toJsonStr(updateGuidanceDTO.getLearningTaskCompletion()));
        guidance.setGuidanceNote(JSONUtil.toJsonStr(updateGuidanceDTO.getGuidanceNote()));
        guidance.setStatus(StringConst.GUIDANCE_FINISH_STATUS);
        boolean update = updateById(guidance);
        if (!update) {
            return R.Failed(DB_UPDATE_DATA_FAIL.getCode(), "修改周指导记录失败");
        }
        return R.Success("修改周指导记录成功", null);
    }

    /**
     * 根据id获取周指导记录详情
     *
     * @param guidanceId
     * @return
     */
    @Override
    public R<GuidanceVO> getGuidanceById(Integer guidanceId) {
        Guidance guidance = getById(guidanceId);
        if (ObjectUtils.isEmpty(guidance)) {
            return R.Failed(DB_GET_DATA_FAIL.getCode(), "根据周指导记录id未能找到对应信息");
        }
        GuidanceVO guidanceVO = BeanCopyUtil.copy(guidance, GuidanceVO.class);
        List<Weekly> weeklyList = weeklyService.getWeeklyListByHonoraryTeacherId(guidance.getSemesterId(), guidance.getHonoraryTeacherId(), guidance.getWeek());
        //判断周报是否完成评阅
        List<Weekly> noReviewStudent = weeklyList.stream().filter(weekly -> !Objects.equals(weekly.getStatus(), StringConst.WEEKLY_REVIEW)).collect(Collectors.toList());
        if (noReviewStudent.size() > 0) {
            List<Integer> noReviewStudentIds = noReviewStudent.stream().map(Weekly::getStudentId).collect(Collectors.toList());
            List<User> noReviewUsers = userService.listByIds(noReviewStudentIds);
            String studentInfo = noReviewUsers.stream().map(user -> user.getUsername() + " " + user.getFullName()).collect(Collectors.joining("，"));
            return R.Failed("学生" + studentInfo + "未完成周报评阅");
        }
        //获取当前教师指导的学生信息
        List<GuidanceVO.GuidanceInfo> studentInfo = weeklyList.stream().map(weekly -> {
            GuidanceVO.GuidanceInfo guidanceInfo = new GuidanceVO.GuidanceInfo();
            guidanceInfo.setStudentId(weekly.getStudentId());
            User student = userService.getById(weekly.getStudentId());
            guidanceInfo.setStudentAccount(student.getUsername());
            guidanceInfo.setStudentName(student.getFullName());
            return guidanceInfo;
        }).collect(Collectors.toList());
        guidanceVO.setGuideStudents(studentInfo);
        //获取项目信息
        List<ProjectVO> projectList = weeklyProjectService.getProjectByWeeklyId(weeklyList.stream().map(Weekly::getId).collect(Collectors.toList()));
        List<String> developmentProject = projectList.stream().map(ProjectVO::getProjectName).distinct().collect(Collectors.toList());
        guidanceVO.setDevelopmentProject(developmentProject);
        //根据学期id获取学期信息
        Semester semester = semesterService.getById(guidance.getSemesterId());
        if (ObjectUtils.isNotEmpty(semester)) {
            guidanceVO.setSemesterName(semester.getName());
        }
        //根据教师id获取教师名称
        User user = teacherService.getById(guidance.getHonoraryTeacherId());
        if (ObjectUtils.isNotEmpty(user)) {
            guidanceVO.setTeacherAccount(user.getUsername());
            guidanceVO.setTeacherName(user.getFullName());
        }
        if (guidance.getStatus().equals(StringConst.GUIDANCE_FINISH_STATUS)) {
            //获取项目任务完成情况
            guidanceVO.setProjectCompletion(com.gcxy.utils.JSONUtil.stringMapperTList(guidance.getProjectCompletion(), GuidanceVO.GuidanceInfo.class));
            //获取学习任务完成情况
            guidanceVO.setLearningTaskCompletion(com.gcxy.utils.JSONUtil.stringMapperTList(guidance.getLearningTaskCompletion(), GuidanceVO.GuidanceInfo.class));
            //周指导记录情况
            guidanceVO.setGuidanceNote(com.gcxy.utils.JSONUtil.stringMapperTList(guidance.getGuidanceNote(), GuidanceVO.GuidanceInfo.class));
        } else {
            //获取项目任务完成情况
            List<GuidanceVO.GuidanceInfo> projectCompletion = weeklyList.stream().map(weekly -> {
                GuidanceVO.GuidanceInfo guidanceInfo = new GuidanceVO.GuidanceInfo();
                guidanceInfo.setStudentId(weekly.getStudentId());
                User student = userService.getById(weekly.getStudentId());
                guidanceInfo.setStudentAccount(student.getUsername());
                guidanceInfo.setStudentName(student.getFullName());
                guidanceInfo.setContent(weekly.getTaskComplete());
                return guidanceInfo;
            }).collect(Collectors.toList());
            guidanceVO.setProjectCompletion(projectCompletion);
            //获取学习任务完成情况
            List<GuidanceVO.GuidanceInfo> learningTaskCompletion = weeklyList.stream().map(weekly -> {
                GuidanceVO.GuidanceInfo guidanceInfo = new GuidanceVO.GuidanceInfo();
                guidanceInfo.setStudentId(weekly.getStudentId());
                User student = userService.getById(weekly.getStudentId());
                guidanceInfo.setStudentAccount(student.getUsername());
                guidanceInfo.setStudentName(student.getFullName());
                guidanceInfo.setContent(weekly.getLearningTaskCompletion());
                return guidanceInfo;
            }).collect(Collectors.toList());
            guidanceVO.setLearningTaskCompletion(learningTaskCompletion);
            //周指导记录情况
            List<GuidanceVO.GuidanceInfo> guidanceNote = weeklyList.stream().map(weekly -> {
                GuidanceVO.GuidanceInfo guidanceInfo = new GuidanceVO.GuidanceInfo();
                guidanceInfo.setStudentId(weekly.getStudentId());
                User student = userService.getById(weekly.getStudentId());
                guidanceInfo.setStudentAccount(student.getUsername());
                guidanceInfo.setStudentName(student.getFullName());
                guidanceInfo.setContent(appraiseService.getById(weekly.getAppraiseId()).getContent());
                return guidanceInfo;
            }).collect(Collectors.toList());
            guidanceVO.setGuidanceNote(guidanceNote);
        }
        return R.Success(guidanceVO);
    }

    /**
     * 分页查询周指导记录
     *
     * @param guidancePageDTO
     * @return
     */
    @Override
    public R<PageVO<GuidanceVO>> getGuidanceByPage(GuidancePageDTO guidancePageDTO) {
        Page<Guidance> page = new Page<>(guidancePageDTO.getPageNo(), guidancePageDTO.getPageSize());
        List<Integer> honoraryTeacherIds = new ArrayList<>();
        //当前用户为教师时，且没有查看组织的权限，则只能查看自己的周指导记录
        UserVO userInfo = userService.getUserInfo();
        boolean isOwnAgencyGuidance = userInfo.getUserResource().stream().anyMatch(resourceVO -> resourceVO.getResourcePath().equals(PermissionConst.GET_OWN_AGENCY_GUIDANCE));
        List<RoleVO> roles = userInfo.getRoles();
        boolean isTeacher = roles.stream().anyMatch(role -> role.getId().equals(TEACHER));
        if (!isOwnAgencyGuidance && isTeacher) {
            honoraryTeacherIds.add(userInfo.getId());
        } else {
            if (ObjectUtils.isNotEmpty(guidancePageDTO.getTeacherName())) {
                //根据教师名称模糊插叙名誉教师信息
                List<User> userList = userService.getUserListByUserName(guidancePageDTO.getTeacherName());
                if (ObjectUtils.isNotEmpty(userList)) {
                    //根据查询结果获取周指导记录id
                    honoraryTeacherIds = userList.stream().map(User::getId).collect(Collectors.toList());
                    //当根据教师名称查询结果为空时，直接返回空
                    if (CollectionUtils.isEmpty(honoraryTeacherIds)) {
                        return R.Success(PageVO.getPageVo(page, GuidanceVO.class));
                    }
                } else {
                    return R.Success(PageVO.getPageVo(page, GuidanceVO.class));
                }
            }
        }
        //根据查询结果获取周指导记录honoraryTeacherIds
        LambdaQueryWrapper<Guidance> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ObjectUtils.isNotEmpty(guidancePageDTO.getSemesterId()), Guidance::getSemesterId, guidancePageDTO.getSemesterId())
                .eq(ObjectUtils.isNotEmpty(guidancePageDTO.getStatus()), Guidance::getStatus, guidancePageDTO.getStatus())
                .eq(ObjectUtils.isNotEmpty(guidancePageDTO.getWeekNum()), Guidance::getWeek, guidancePageDTO.getWeekNum())
                .eq(ObjectUtils.isNotEmpty(guidancePageDTO.getAgencyId()), Guidance::getAgencyId, guidancePageDTO.getAgencyId())
                .in(CollectionUtils.isNotEmpty(honoraryTeacherIds), Guidance::getHonoraryTeacherId, honoraryTeacherIds)
                .orderByDesc(Guidance::getUpdateTime);
        Page<Guidance> guidancePage = page(page, queryWrapper);
        PageVO<GuidanceVO> pageVO = new PageVO<>();
        BeanUtils.copyProperties(guidancePage, pageVO);
        List<GuidanceVO> guidanceVOList = guidancePage.getRecords().stream().map(guidance -> {
            GuidanceVO guidanceVO = BeanCopyUtil.copy(guidance, GuidanceVO.class);
            List<Weekly> weeklyList = weeklyService.getWeeklyListByHonoraryTeacherId(guidance.getSemesterId(), guidance.getHonoraryTeacherId(), guidance.getWeek());
            guidanceVO.setGuideStudents(getStudentInfo(weeklyList));
            guidanceVO.setNoViewStudents(getNoViewStudentInfo(weeklyList));
            guidanceVO.setSemesterName(semesterService.getById(guidance.getSemesterId()).getName());
            User honoraryTeacherInfo = teacherService.getById(guidance.getHonoraryTeacherId());
            guidanceVO.setTeacherName(honoraryTeacherInfo.getFullName());
            guidanceVO.setTeacherAccount(honoraryTeacherInfo.getUsername());
            return guidanceVO;
        }).collect(Collectors.toList());
        pageVO.setRecords(guidanceVOList);
        return R.Success(pageVO);
    }

    /**
     * 获取周报未评阅学生信息
     *
     * @param weeklyList
     * @return
     */
    private List<GuidanceVO.GuidanceInfo> getNoViewStudentInfo(List<Weekly> weeklyList) {
        List<Weekly> noReviewStudent = weeklyList.stream().filter(weekly -> !Objects.equals(weekly.getStatus(), StringConst.WEEKLY_REVIEW)).collect(Collectors.toList());
        if (noReviewStudent.size() > 0) {
            return noReviewStudent.stream().map(weekly -> {
                GuidanceVO.GuidanceInfo guidanceInfo = new GuidanceVO.GuidanceInfo();
                guidanceInfo.setStudentId(weekly.getStudentId());
                User student = userService.getById(weekly.getStudentId());
                guidanceInfo.setStudentAccount(student.getUsername());
                guidanceInfo.setStudentName(student.getFullName());
                guidanceInfo.setContent(weekly.getLearningTaskCompletion());
                return guidanceInfo;
            }).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    /**
     * 获取当前教师指导的学生信息
     *
     * @param weeklyList
     * @return
     */
    private List<GuidanceVO.GuidanceInfo> getStudentInfo(List<Weekly> weeklyList) {
        return weeklyList.stream().map(weekly -> {
            GuidanceVO.GuidanceInfo guidanceInfo = new GuidanceVO.GuidanceInfo();
            guidanceInfo.setStudentId(weekly.getStudentId());
            User student = userService.getById(weekly.getStudentId());
            guidanceInfo.setStudentAccount(student.getUsername());
            guidanceInfo.setStudentName(student.getFullName());
            return guidanceInfo;
        }).collect(Collectors.toList());
    }

    @Override
    public R<?> createGuidanceFile(DownloadFileDTO dto) {
        LambdaQueryWrapper<Guidance> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Guidance::getAgencyId, dto.getAgencyId())
                .eq(Guidance::getSemesterId, dto.getSemesterId())
                .ge(Guidance::getWeek, dto.getBeginWeekNum())
                .le(Guidance::getWeek, dto.getEndWeekNum())
                .eq(Guidance::getStatus, StringConst.GUIDANCE_FINISH_STATUS);
        List<Guidance> guidanceList = list(wrapper);
        Semester semester = semesterService.getById(dto.getSemesterId());
        for (Guidance guidance : guidanceList) {
            try {
                //生成映射数据
                Map<String, Object> params = generateGuidanceMapData(guidance, dto, semester.getName());
                String teacherName = userService.getById(guidance.getHonoraryTeacherId()).getFullName();
                //获取学生班级信息
                List<GuidanceVO.GuidanceInfo> rvos = com.gcxy.utils.JSONUtil.stringMapperTList(guidance.getGuideStudents(), GuidanceVO.GuidanceInfo.class);
                if (CollectionUtils.isEmpty(rvos)) {
                    return R.Failed("异步生成文件失败：{}", teacherName + "老师第" + guidance.getWeek() + "周周指导记录中学生信息存在问题");
                }
                List<Integer> studentIds = rvos.stream().map(GuidanceVO.GuidanceInfo::getStudentId).collect(Collectors.toList());
                List<User> studentInfoList = userService.listByIds(studentIds);
                if (CollectionUtils.isEmpty(studentInfoList)) {
                    return R.Failed("异步生成文件失败：{}", teacherName + "老师第" + guidance.getWeekTime() + "周周指导记录中学生信息存在问题");
                }
                List<String> classInfos = studentInfoList.stream().map(user -> {
                    return clazzService.getById(user.getClassId()).getClassName();
                }).distinct().collect(Collectors.toList());
                if (CollectionUtils.isEmpty(classInfos)) {
                    return R.Failed("异步生成文件失败：{}", teacherName + "老师第" + guidance.getWeekTime() + "周周指导记录学生中班级信息存在问题");
                }
                //文件名
                String fileName = teacherName + "-第" + guidance.getWeekTime() + "周教师周指导记录" + ".docx";
                List<String> filePaths = new ArrayList<>();
                for (String classInfo : classInfos) {
                    //电子版路径
                    String filePathElectronic = DEFAULT_FILE_PATH + DEFAULT_GUIDANCE_EVALUATE_PATH + classInfo + DEFAULT_GUIDANCE_PATH + teacherName + "/" + fileName;
                    //纸质版路径
//                    String filePathPaper = DEFAULT_FILE_PATH + classInfo + DEFAULT_GUIDANCE_PATH + teacherName + "/" + fileName;
                    filePaths.add(filePathElectronic);
//                    filePaths.add(filePathPaper);
                }
                XWPFUtil.createWord(filePaths, params);
            } catch (Exception e) {
                return R.Failed("异步生成文件失败：{}", e.getMessage());
            }
        }
        return R.Success();
    }

    /**
     * 生成周指导记录映射数据
     *
     * @param guidance
     * @return
     */
    private Map<String, Object> generateGuidanceMapData(Guidance guidance, DownloadFileDTO dto, String semesterName) {
        Map<String, Object> params = new HashMap<>();
        params.put("${semester}", semesterName);
        String[] weeklyTime = guidance.getWeekTime().split("~");
        params.put("${dateTime}", "从" + weeklyTime[0] + "到" + weeklyTime[1]);
        params.put("${weekNum}", guidance.getWeek().toString());
        params.put("${teacherName}", userService.getById(guidance.getHonoraryTeacherId()).getFullName());
        params.put("${guideStudents}", packageStudentData(guidance.getGuideStudents()));
        String developmentProject = guidance.getDevelopmentProject().replace("[", "").replace("]", "").replace("\"", "").replace(",", "、");
        params.put("${developmentProject}", developmentProject);
        params.put("${projectCompletion}", packageData(guidance.getProjectCompletion(), false));
        params.put("${learningTaskCompletion}", packageData(guidance.getLearningTaskCompletion(), false));
        params.put("${guidanceNote}", packageData(guidance.getGuidanceNote(), false));
        //根据当前周教师对应学生的是周报对应的周教师评价
        String teacherAppraise = weeklyService.getAppraise(dto.getSemesterId(), guidance.getHonoraryTeacherId(), guidance.getWeek());
        params.put("${teacherAppraise}", teacherAppraise);
        return params;
    }

    /**
     * 封装数据
     *
     * @param oldData
     * @param isAddPercentage 是否增加百分比
     * @return
     */
    private String packageData(String oldData, boolean isAddPercentage) {
        List<GuidanceVO.GuidanceInfo> rvos = com.gcxy.utils.JSONUtil.stringMapperTList(oldData, GuidanceVO.GuidanceInfo.class);
        if (CollectionUtils.isEmpty(rvos)) {
            return null;
        }
        StringBuilder result = new StringBuilder();
        for (GuidanceVO.GuidanceInfo rvo : rvos) {
            result.append(rvo.getStudentName()).append("：").append(rvo.getContent());
            if (isAddPercentage) {
                result.append("-100%").append("\n");
            } else {
                result.append("\n");
            }
        }
        return result.toString();
    }

    /**
     * 封装学生信息数据
     *
     * @param oldData
     * @return
     */
    private String packageStudentData(String oldData) {
        List<GuidanceVO.GuidanceInfo> rvos = com.gcxy.utils.JSONUtil.stringMapperTList(oldData, GuidanceVO.GuidanceInfo.class);
        if (CollectionUtils.isEmpty(rvos)) {
            return null;
        }
        StringBuilder result = new StringBuilder();
        for (GuidanceVO.GuidanceInfo rvo : rvos) {
            result.append(rvo.getStudentName()).append("：").append(rvo.getStudentAccount());
            result.append("\n");
        }
        return result.toString();
    }

    /**
     * 获取周指导记录
     *
     * @param honoraryTeacherId
     * @param weekNum
     * @param semesterId
     * @return
     */
    @Override
    public Guidance getGuidanceInfo(Integer honoraryTeacherId, Integer weekNum, Integer semesterId) {
        LambdaQueryWrapper<Guidance> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Guidance::getHonoraryTeacherId, honoraryTeacherId).eq(Guidance::getWeek, weekNum).eq(Guidance::getSemesterId, semesterId);
        return getOne(wrapper);

    }
}
